package com.shuwen.gcdj.common.util;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.shuwen.gcdj.common.exception.ApiException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.cglib.beans.BeanMap;

import java.beans.PropertyDescriptor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

public class BeanHelper {
    private static final Logger log = LoggerFactory.getLogger(com.shuwen.gcdj.common.util.BeanHelper.class);
    private static final String DATAS_JSON = "{\"datas\":%s}";

    private BeanHelper() {
    }

    public static void copyProperties(Object source, Object target) {
        if (source != null && target != null) {
            String[] properties = getCopyFields(source.getClass(), target.getClass());
            copyProperties(source, target, properties);
        }
    }

    /**
     * 从List<A> copy到List<B>
     * @param list List<B>
     * @param clazz B
     * @return List<B>
     */
    public static <T> List<T> copyList(List<?> list,Class<T> clazz){
        return JSONUtil.toList(JSONUtil.parseArray(list), clazz);
    }

    public static void copyProperties(Object source, Object target, String[] properties) {
        if (source != null && target != null) {
            BeanWrapper src = new BeanWrapperImpl(source);
            BeanWrapper trg = new BeanWrapperImpl(target);
            String[] var6 = properties;
            int var7 = properties.length;

            for(int var8 = 0; var8 < var7; ++var8) {
                String propertyName = var6[var8];

                try {
                    Object value = src.getPropertyValue(propertyName);
                    if (value instanceof String) {
                        value = ((String)value).trim();
                    }

                    trg.setPropertyValue(propertyName, value);
                } catch (BeansException var11) {
                }
            }
        }

    }

    public static void copyPropertiesNoNull(Object source, Object target) {
        if (source != null && target != null) {
            String[] properties = getCopyFields(source.getClass(), target.getClass());
            copyPropertiesNoNull(source, target, properties);
        }

    }

    public static void copyPropertiesNoNull(Object source, Object target, String[] properties) {
        if (source != null && target != null) {
            BeanWrapper src = new BeanWrapperImpl(source);
            BeanWrapper trg = new BeanWrapperImpl(target);
            String[] var6 = properties;
            int var7 = properties.length;

            for(int var8 = 0; var8 < var7; ++var8) {
                String propertyName = var6[var8];

                try {
                    Object value = src.getPropertyValue(propertyName);
                    if (value instanceof String) {
                        value = ((String)value).trim();
                    } else if (value == null && src.getPropertyType(propertyName) == String.class) {
                        value = "";
                    }

                    trg.setPropertyValue(propertyName, value);
                } catch (BeansException var11) {
                }
            }
        }

    }

    public static String[] getCopyFields(Class<?> source, Class<?> target) {
        Field[] sourceDeclaredFields = getDeclaredNotSyntheticFields(source);
        Field[] targetDeclaredFields = getDeclaredNotSyntheticFields(target);
        String[] properties;
        if (sourceDeclaredFields.length > targetDeclaredFields.length) {
            properties = getInverseFields(target, (String[])null);
        } else {
            properties = getInverseFields(source, (String[])null);
        }

        return properties;
    }

    public static String[] getInverseFields(Class<?> clz, String[] excludedFields) {
        Field[] declaredFields = getDeclaredNotSyntheticFields(clz);
        List<String> fieldList = new ArrayList();
        if (excludedFields != null && excludedFields.length != 0) {
            List<String> excludedlist = Arrays.asList(excludedFields);
            Field[] var12 = declaredFields;
            int var13 = declaredFields.length;

            for(int var8 = 0; var8 < var13; ++var8) {
                Field f = var12[var8];
                String fieldName = f.getName();
                if (!excludedlist.contains(fieldName)) {
                    fieldList.add(fieldName);
                }
            }
        } else {
            Field[] var4 = declaredFields;
            int var5 = declaredFields.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                Field f = var4[var6];
                fieldList.add(f.getName());
            }
        }

        return (String[])fieldList.toArray(new String[0]);
    }

    public static String getFieldName(String propertyName, Class<?> clz) {
        if (propertyName == null) {
            return null;
        } else {
            Field[] declaredFields = getDeclaredNotSyntheticFields(clz);
            Field[] var4 = declaredFields;
            int var5 = declaredFields.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                Field f = var4[var6];
                String fieldName = f.getName();
                if (fieldName.equalsIgnoreCase(propertyName)) {
                    return fieldName;
                }
            }

            return null;
        }
    }

    public static Method getGetMethod(String propertyName, Class<?> clz) {
        if (propertyName == null) {
            return null;
        } else {
            Method[] declaredMethods = clz.getMethods();
            Method[] var3 = declaredMethods;
            int var4 = declaredMethods.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Method d = var3[var5];
                if (d.getName().equalsIgnoreCase("get" + propertyName) || d.getName().equalsIgnoreCase("is" + propertyName)) {
                    return d;
                }
            }

            return null;
        }
    }

    /*public static String[] getProperties(HttpServletRequest request, Class<?> clz) {
        Map<String, Object> datas = getEntity(request);
        return getProperties(datas, clz);
    }*/

    public static String[] getNullPropertyNames(Object source) {
        BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set<String> emptyNames = new HashSet();
        PropertyDescriptor[] var4 = pds;
        int var5 = pds.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            PropertyDescriptor pd = var4[var6];
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }

        String[] result = new String[emptyNames.size()];
        return (String[])emptyNames.toArray(result);
    }

    public static String[] getNullOrEmptyPropertyNames(Object source) {
        BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set<String> emptyNames = new HashSet();
        PropertyDescriptor[] var4 = pds;
        int var5 = pds.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            PropertyDescriptor pd = var4[var6];
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            } else if (srcValue instanceof String && StringUtils.isBlank(String.valueOf(srcValue))) {
                emptyNames.add(pd.getName());
            }
        }

        String[] result = new String[emptyNames.size()];
        return (String[])emptyNames.toArray(result);
    }

    /*public static String getRequestBody(HttpServletRequest request) throws IOException {
        String src;
        try {
            src = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
        } catch (IllegalStateException var3) {
            src = IOUtils.toString(request.getReader());
            log.error("BeanHelper.getRequestBody() error:", var3);
        }

        return src;
    }*/

    public static String bean2Json(Object bean) {
        String result = "";

        try {
            result = pBean2Json(bean, JsonInclude.Include.ALWAYS);
        } catch (IOException var3) {
            log.error("pBean2Json() error:", var3);
        }

        return result;
    }

    public static String bean2Json(Object bean, JsonInclude.Include includeType) {
        String result = "";

        try {
            result = pBean2Json(bean, includeType);
        } catch (IOException var4) {
            log.error("pBean2Json() error:", var4);
        }

        return result;
    }

    public static <T> T json2Bean(String json, Class<T> clz) {
        T bean = null;
        if (StringUtils.isNotBlank(json)) {
            try {
                bean = pJson2Bean(json, clz);
            } catch (Exception var4) {
                log.error("pJson2Bean() error:", var4);
            }
        }

        return bean;
    }

    public static <T> T json2Bean(String json, Class<?> clz, Class<?>... parameterizedTypeClass) {
        T bean = null;
        if (StringUtils.isNotBlank(json)) {
            try {
                bean = pJson2Bean(json, clz, parameterizedTypeClass);
            } catch (Exception var5) {
                log.error("pJson2Bean() error:", var5);
            }
        }

        return bean;
    }

    public static String bean2csv(Object bean) {
        StringBuilder buffer = new StringBuilder(8192);
        Field[] declaredFields = getDeclaredNotSyntheticFields(bean.getClass());
        BeanWrapper src = new BeanWrapperImpl(bean);
        Field[] var5 = declaredFields;
        int var6 = declaredFields.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Field f = var5[var7];

            try {
                Object value = src.getPropertyValue(f.getName());
                buffer.append("\"").append(value).append("\",");
            } catch (Exception var10) {
                log.error(var10.getMessage(), var10);
            }
        }

        String result = "";
        if (buffer.length() > 0) {
            result = buffer.substring(0, buffer.length() - 1);
        }

        return result;
    }

    public static String bean2csv(Object bean, String[] excludedFields) {
        StringBuilder buffer = new StringBuilder(8192);
        String[] properties = getInverseFields(bean.getClass(), excludedFields);
        BeanWrapper src = new BeanWrapperImpl(bean);
        String[] var6 = properties;
        int var7 = properties.length;

        for(int var8 = 0; var8 < var7; ++var8) {
            String propertyName = var6[var8];

            try {
                Object value = src.getPropertyValue(propertyName);
                buffer.append("\"").append(value).append("\",");
            } catch (Exception var11) {
                log.error(var11.getMessage(), var11);
            }
        }

        String result = "";
        if (buffer.length() > 0) {
            result = buffer.substring(0, buffer.length() - 1);
        }

        return result;
    }

    public static String convertToJsonData(List<?> listOfObject) {
        String resultJson = bean2Json(listOfObject);
        return String.format("{\"datas\":%s}", resultJson);
    }

    public static <T> T mapToBean(Map<String, Object> map, Class<T> clazz) {
        T bean = newInstance(clazz);
        BeanMap.create(bean).putAll(map);
        return bean;
    }

    public static Map<String, Object> beanToMap(Object bean) {
        return null == bean ? null : BeanMap.create(bean);
    }

    public static Map<String, Object> beanToNotNullValueMap(Object bean) {
        Map<String, Object> map = beanToMap(bean);
        return StreamUtil.filterNotNullValueMap(map);
    }

    public static String[] copyModifiedProperties(Object source, Object target) {
        String[] properties = getModifiedProperties(source, target);
        copyProperties(source, target, properties);
        return properties;
    }

    public static <T> T copyModifiedPropertiesToTarget(Object source, T target) {
        String[] properties = getModifiedProperties(source, target);
        copyProperties(source, target, properties);
        String[] inverseFields = getInverseFields(target.getClass(), properties);
        BeanWrapper trg = new BeanWrapperImpl(target);
        String[] var5 = inverseFields;
        int var6 = inverseFields.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            String propertyName = var5[var7];

            try {
                trg.setPropertyValue(propertyName, (Object)null);
            } catch (Exception var10) {
                log.error(var10.getMessage(), var10);
            }
        }

        return target;
    }

    public static String[] getModifiedProperties(Object source, Object target) {
        String[] properties = getCommonFields(source.getClass(), target.getClass());
        List<String> result = new ArrayList();
        BeanWrapper src = new BeanWrapperImpl(source);
        BeanWrapper trg = new BeanWrapperImpl(target);
        String[] var9 = properties;
        int var10 = properties.length;

        for(int var11 = 0; var11 < var10; ++var11) {
            String propertyName = var9[var11];

            try {
                Object sourceValue = src.getPropertyValue(propertyName);
                Object targetValue = trg.getPropertyValue(propertyName);
                if (sourceValue instanceof String) {
                    sourceValue = ((String)sourceValue).trim();
                }

                if (targetValue instanceof String) {
                    targetValue = ((String)targetValue).trim();
                }

                boolean equal = sourceValue != null && !sourceValue.equals(targetValue) || targetValue != null && !targetValue.equals(sourceValue);
                if (equal) {
                    result.add(propertyName);
                }
            } catch (BeansException var14) {
            }
        }

        return (String[])result.toArray(new String[0]);
    }

    public static String[] getCommonFields(Class<?> sourceClz, Class<?> targetClz) {
        Field[] sourceDeclaredFields = getDeclaredNotSyntheticFields(sourceClz);
        Field[] targetDeclaredFields = getDeclaredNotSyntheticFields(targetClz);
        List<String> fieldList = new ArrayList();
        Field[] var5 = sourceDeclaredFields;
        int var6 = sourceDeclaredFields.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Field s = var5[var7];
            Field[] var9 = targetDeclaredFields;
            int var10 = targetDeclaredFields.length;

            for(int var11 = 0; var11 < var10; ++var11) {
                Field t = var9[var11];
                String fieldName = s.getName();
                if (fieldName != null && fieldName.equals(t.getName())) {
                    fieldList.add(fieldName);
                    break;
                }
            }
        }

        return (String[])fieldList.toArray(new String[0]);
    }

    /*public static Map<String, Object> getEntity(HttpServletRequest request) {
        Map datas;
        try {
            datas = (Map)(new ObjectMapper()).readValue(getRequestBody(request), new TypeReference<Map<String, Object>>() {
            });
        } catch (IOException var3) {
            datas = null;
            log.warn("new ObjectMapper().readValue() error:", var3);
        }

        return datas;
    }*/

    private static <T> T newInstance(Class<T> targetClass) {
        try {
            T tmpBean = targetClass.newInstance();
            return tmpBean;
        } catch (Exception var3) {
            log.error("targetClass.newInstance() error:", var3);
            throw new ApiException("global.service.internal.error");
        }
    }

    private static String pBean2Json(Object bean, JsonInclude.Include includeType) throws IOException {
        String json = null;
        if (bean != null) {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.registerModule(new JavaTimeModule());
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(outputStream, JsonEncoding.UTF8);
            objectMapper.setSerializationInclusion(includeType);
            objectMapper.writeValue(jsonGenerator, bean);
            json = outputStream.toString("UTF-8");
        }

        return json;
    }

    private static <T> T pJson2Bean(String json, Class<T> clz) throws IOException {
        ObjectMapper objectMapper = getObjectMapper();
        return objectMapper.readValue(json, clz);
    }

    private static <T> T pJson2Bean(String json, Class<?> clz, Class<?>... parameterizedTypeClass) throws IOException {
        ObjectMapper objectMapper = getObjectMapper();
        JavaType javaType = objectMapper.getTypeFactory().constructParametricType(clz, parameterizedTypeClass);
        return objectMapper.readValue(json, javaType);
    }

    private static String[] getProperties(Map<String, Object> datas, Class<?> clz) {
        Field[] fields = getDeclaredNotSyntheticFields(clz);
        List<String> propList = new ArrayList();
        if (datas != null) {
            Field[] var7 = fields;
            int var8 = fields.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                Field field = var7[var9];
                JsonIgnore jsonIgnore = (JsonIgnore)field.getAnnotation(JsonIgnore.class);
                if (jsonIgnore == null) {
                    JsonProperty jsonProperty = (JsonProperty)field.getAnnotation(JsonProperty.class);
                    String name;
                    if (jsonProperty == null) {
                        name = field.getName();
                    } else {
                        name = jsonProperty.value();
                    }

                    if (datas.get(name) != null) {
                        propList.add(field.getName());
                    }
                }
            }
        }

        return (String[])propList.toArray(new String[0]);
    }

    public static Field[] getDeclaredNotSyntheticFields(Class<?> clz) {
        Field[] declaredFields = clz.getDeclaredFields();
        Stream<Field> stream = Arrays.stream(declaredFields);
        Field[] filterFields = (Field[])stream.filter((f) -> {
            if (f.isSynthetic() && !"$jacocoData".equalsIgnoreCase(f.getName())) {
                log.info("need to check unexpected synthetic field: " + clz.getName() + "%" + f.getName());
            }

            return !f.isSynthetic();
        }).toArray((x$0) -> {
            return new Field[x$0];
        });
        return filterFields;
    }

    private static ObjectMapper getObjectMapper() {
        return com.shuwen.gcdj.common.util.BeanHelper.Singleton.INSTANCE.getInstance();
    }

    static enum Singleton {
        INSTANCE;

        private final ObjectMapper objectMapper = new ObjectMapper();

        private Singleton() {
            this.objectMapper.registerModule(new JavaTimeModule());
            this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        }

        public ObjectMapper getInstance() {
            return this.objectMapper;
        }
    }

    /**
     * 分页
     * @param count 总条数
     * @param size 单页数据
     * @return
     */
    public static Integer getCeiling(Integer count, Integer size) {
        if (count == 0 || size == 0) {
            return 0;
        }
        double value = Double.valueOf(count) / Double.valueOf(size);
        return count > 0 ? (int)Math.ceil(value) : 0;
    }
}
